////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma once

#ifndef __WN_MATH_INTERNAL_X86_AVX_ELEMENTS_H__
#define __WN_MATH_INTERNAL_X86_AVX_ELEMENTS_H__

#ifndef __WN_MATH_INTERNAL_ELEMENTS_H__
    #error "Internal/x86/AVX/WNElements.h should never be included directly."
#elif !defined _WN_X86
    #error "Internal/x86/AVX/WNElements.h has been included on a non x86 platform. Please rectify this."
#elif !defined __WN_MATH_ENABLE_EXTENSIONS
    #error "Internal/x86/AVX/WNElements.h has been included when extensions are disabled. Please rectify this."
#elif !defined __WN_MATH_AVX_ALLOWED
    #error "Internal/x86/AVX/WNElements.h has been included when AVX is not allowed. Please rectify this."
#endif

#ifdef _WN_MSVC
    #pragma warning(push)
    #pragma warning(disable: 4987)
#endif

#include <immintrin.h>

#ifdef _WN_MSVC
    #pragma warning(pop)
#endif

namespace WNMath {
    namespace __WNInternal {
        #ifdef __WN_MATH_AVX_AVAILABLE
            template <WN_UINT32 Dimension>
            struct WN_ALIGN(32) __WNElements<WN_FLOAT32, Dimension, typename WNCore::WNEnableWhen<(Dimension > 4)>::Value> {
                WN_STATIC_ASSERT_DESC(Dimension > 0, "Must have an element base of at least 1");

                union {
                    WN_FLOAT32 mValues[Dimension];
                    __m256 mYMMValues[Dimension & ~3];
                    __m128 mXMMValues[(Dimension + 3) >> 2];
                };
            };

            template <WN_UINT32 Dimension>
            struct WN_ALIGN(32) __WNElements<WN_FLOAT64, Dimension, typename WNCore::WNEnableWhen<(Dimension > 2)>::Value> {
                WN_STATIC_ASSERT_DESC(Dimension > 0, "Must have an element base of at least 1");

                union {
                    WN_FLOAT64 mValues[Dimension];
                    __m256d mYMMValues[Dimension & ~1];
                    __m128d mXMMValues[(Dimension + 1) >> 1];
                };
            };
        #endif
    }
}

#endif // __WN_MATH_INTERNAL_X86_AVX_ELEMENTS_H__